/* 

==========================================================

DX490a - Summer 2010

Instructor: Stelios Manousakis

==========================================================

Class 17.2:

SuperCollider Audio Unit plug-ins

Contents:

• About Audio Unit (AU) plug-ins

• scsynth as an Audio Unit (AU) plug-in

- Installation

- Using scsynth as an AU

• Compiling 'standalone' Audio Unit (AU) plug-ins from within SC

- Example: creating an SC Audio Unit plugin

==========================================================

*/





// ================= CREATING AUDIO UNIT PLUG-INS WITH SC =================


// ====== ABOUT AUDIO UNIT PLUG-INS ======


// Here is the wikipedia explanation: 

// "Audio Units (AU) are a system-level plug-in architecture provided by Core Audio in Mac OS X developed by Apple Computer. Audio Units are a set of application programming interface services provided by the operating system to generate, process, receive, or otherwise manipulate streams of audio in near-real-time with minimal latency. It may be thought of as Apple's architectural equivalent to the other popular plug-in format, Steinberg's VST."


// You can read more about Core Audio and AU Units in Apple's Core Audio page:

"open http://developer.apple.com/iphone/library/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003577-CH1-SW1".unixCmd



// There are two ways in which you can use SC code as an AU plugin from any host program in Mac OSX that supports Audio Units.

// 1. Using SuperCollider servers from within the host.  

// 2. Compiling an AU plugin from SC using the AudioUnitBuilder quark. 



// This will create an autonomous plug-in for you that you can use and share without having to open SC, but having all its parameters being accesible and setable from your host.


// Both these methods use the "SuperColliderAU.component" which comes together with SC and can be found in the application's main directory. 



// Here is a list of some (not all) programs supporting AUs:

"open http://swiki.hfbk-hamburg.de:8888/MusicTechnology/823".unixCmd




// ====== SCSYNTH AS AN AUDIO UNIT PLUGIN ======


// This method loads an scsynth server inside the host; each embedded scsynth (you can have many) is assigned a specific port in the loopback IP "127.0.0.1", through which you can talk to it as you would with a server inside your SC, or in a remote machine. By default this plugin does nothing. You need to write your own code in sclang and send messages to the specific scsynth instance, and make things happen inside your host application. This requires working in both your host and SC at the same time.



// ------ Installation --

// The first thing you need to do is copy the "SuperColliderAU.component" file into one of these paths:

// Library/Audio/Plug-ins/Components  

// ~/Library/Audio/Plug-ins/Components



// ------ Using scsynth as an AU --

// • 1: In your host application, make a new track and load an instance of the plug-in to it. If the host supportst this plugin (unfortunately, not all do - ex. Reaper v.3.11 doesn't yet), you will see in the plug-ins interface a 'Port nr' indication. This is the OSC port through which you can communicate between the host and SC. Every new instance of the SuperColliderAU that you make will give you a new port, so you can have multiple isntances open.

// • 2: In SC you have to:

// a: Connect to the particular scsynth instance:

~auServer1 = Server(\scau1, NetAddr("127.0.0.1", 9992)); // Change the port from 9992 to the one seen in the plug-ins interface inside your host

~auServer1.serverRunning = true; // turn it on

~auServer1.ping(10); // confirms that communication is working


// >> SC as a Filter/Effect

// Now let's create a quick ring-modulator effect:

z = { AudioIn.ar([1,2]) * SinOsc.ar(MouseX.kr(100, 1000)) }.play(~auServer1);

// You can activate many synths inside the same plug-in. 

// Here's a randomized Comb delay filter

x = { AudioIn.ar([1,2]) + (CombC.ar(AudioIn.ar([1,2]), 1, LFDNoise3.ar(MouseX.kr(0.0001, 100)).range(0.1, 0.9), 0.5))}.play(~auServer1);

z.free; // free it, like you would normally do

x.free; // free it, like you would normally do


// >> SC as a Generator

// Create a generator within your host. 

// Note something strange (tested in Logic): there needs to be a soundfile loaded for the code below to work! Maybe the SuperColliderAU assumes it is a filter, so it only gets activated when there is something to process, regardless of whether it actually uses audio input or not?...

g = { RosslerL.ar(MouseX.kr(20, SampleRate.ir), 0.36, 0.35, 4.5) * 0.3 }.play(~auServer1);

g.free; // free it, like you would normally do

// >> Multiple instances

// create a second instance of the SuperColliderAU plugin, and put it on the same channel

~auServer2 = Server(\scau2, NetAddr("127.0.0.1", 9996)); // Change the port from 9992 to the one seen in the plug-ins interface inside your host

~auServer2.serverRunning = true; // turn it on

~auServer2.ping(10); // confirms that communication is working

// Now, turn on the filter to process the generator's output, within the host's channel!

x = { AudioIn.ar([1,2]) + (CombC.ar(AudioIn.ar([1,2]), 1, LFDNoise3.ar(MouseX.kr(0.0001, 100)).range(0.1, 0.9), 0.5))}.play(~auServer2);





// ====== COMPILING 'STANDALONE' AUDIO UNIT PLUGINS WITHIN SC ======


// You can also create autonomous plug-ins. These compile an instance of scsynth that can be controlled through a standard interface from within the host, without requiring sclang or any other program to control them through OSC. Plugins made in this manner can be opened and used in the same manner as any commercial AU plug-in.


// • First, get the AudioUnitBuilder quark:

Quarks.install( "AudioUnitBuilder", checkoutIfNeeded: false)

// This is a utility class, allowing you to author AU plug-ins from inside sclang!

// • You need to have Apple Developer Tools installed, as this class needs Rez for compiling. This would be located together with the Developer package, for example here: "/Developer/usr/bin/Rez"


CombC.ar(AudioIn.ar([1,2]), 1, LFDNoise3.ar(MouseX.kr(0.0001, 100)).range(0.1, 0.9), 0.5))}.play(~auServer2);



// ------ Example: creating an SC Audio Unit plugin --

// Here is an example, have a look at the AudioUnitBuilder help file for more information

(

var name, func, specs, componentType, componentSubtype, builder;


name = "dx490a_XComb"; // name of the plugin

func =  { 

// arguments that are going to be setable from the AU plug-in's interface

| gain, dry_wet,

  del1TimeMin, del1TimeMax, del1Freq, del1Feedback, inAmount1, crossFdbAmount1,

  del2TimeMin, del2TimeMax, del2Freq, del2Feedback, inAmount2, crossFdbAmount2|

 

  // internal variables

  var comb1, comb2;

var local, in, amp, feedBackMix, out;

var maxDelay = 3;

//Input from AU  host

in = Mix.new(AudioIn.ar([1,2])); 

// Input from the comb filters

local = LocalIn.ar(2); 

   

   /* comb filters with: 

   - input from outside as well as from the other comb filter, with adjustable input gains

   - randomized delay time, with adjustable min/max times and frequency 

   - adjustable feedback

   - amplitude and DC protection

   */

   comb1 = Limiter.ar(LeakDC.ar(CombC.ar((in * inAmount1) + (local[1] * crossFdbAmount1), maxDelay, LFDNoise3.ar(del1Freq).range(del1TimeMin, del1TimeMax), del1Feedback)), 0.985);

   comb2 = Limiter.ar(LeakDC.ar(CombC.ar((in * inAmount2) + (local[0] * crossFdbAmount2), maxDelay, LFDNoise3.ar(del2Freq).range(del2TimeMin, del2TimeMax), del2Feedback)), 0.985);

   

   // dry_wet is a cross-fader: 0 = only dry, 1 = only wet

out = (in * (1-dry_wet)) + ([comb1, comb2] * dry_wet);

   

   // Feedback output of the comb filters

   LocalOut.ar([comb1, comb2]);


//Output to AU host (mono)

Out.ar(0, gain * Mix.new(out)); 

};


// These specs are not SC-related, but AU related. See the AudioUnitBuilder helpfile for more on that:

// Each entry in the specs array should be an array like this: 

// [min, max, display style, default value, unit]

specs =  #[ 

[0, 1 , \Linear, 0.75, \LinearGain] , // gain (overall)

[0, 1 , \Linear, 0.5, \LinearGain] , // dry_wet

[0, 2 , \Linear, 0.0001,\Seconds ] , // del1TimeMin

[0, 2 , \Linear, 0.1,\Seconds ] , // del1TimeMax

[0.01, 200, \Logarithmic, 1, \Hertz], //del1Freq

[-0.9, 0.9 , \Linear, 0.1,\LinearGain ] , // del1Feedback

[0, 1 , \Linear, 0.1,\LinearGain ] , // inAmount1

[-0.9, 0.9 , \Linear, 0.1,\LinearGain ] , // crossFdbAmount1

[0, 2 , \Linear, 0.0001,\Seconds ] , // del2TimeMin

[0, 2 , \Linear, 0.1,\Seconds ] , // del2TimeMax

[0.01, 200, \Logarithmic, 1, \Hertz], //del2Freq

[-0.9, 0.9 , \Linear, 0.1,\LinearGain ] , // del2Feedback

[0, 1 , \Linear, 0.1,\LinearGain ] , // inAmount2

[-0.9, 0.9 , \Linear, 0.1,\LinearGain ] , // crossFdbAmount2


];

// Below is the bookkeeping stuff for compiling the AU plug-in:


// this must be a four chars code. Either \aufx (effect) or \aumf (music effect).

// music effects may receive MIDI but some applications won't recognize them

// default is \aumf

componentType = \aufx;

// this must be a four chars code. It sould be unique in your system within all SuperColliderAU plugins.

componentSubtype = \d490; 


builder = AudioUnitBuilder.new(name, componentSubtype,func, specs, componentType);


//build and install into ~/Library/Audio/Plug-Ins/Components. You SuperColliderAU.component should be located in the same place

builder.makeInstall; 

)


// Now, you can go to your host of choice (try first one of the list above, to make sure it is one that fully supports the SuperColliderAU.component, and try your plug-in out!


// Remember: Any plug-ins you write, need to be open-source, and comply with SuperCollider's GPL license